-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VRF API refactor #137
VRF API refactor #137
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job 👍 ! @tessico , only a few comments
- maybe you can just start a branch from this repo instead of from your fork, (I believe you have write access, right?) so that it's slightly easier for us to switch to your branch without adding another git remote. (thanks!)
- please also take a look at the pain of adding trait bound in the downstream here, we should add these traits to our
trait VrfScheme::PublicKey/SecretKey/Proof
etc) cc @DieracDelta
primitives/src/vrf/mod.rs
Outdated
&self, | ||
pp: &Self::PublicParameter, | ||
proof: &Self::Proof, | ||
public_key: &Self::PublicKey, | ||
input: &Self::Input, | ||
) -> Result<bool, PrimitivesError>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@philippecamacho suggested previously that we should add output: Self::Output
to the input param to verify()
so that the users won't accidentally use the wrong (proof, output) combo.
as you could potentially use a proof, then H_1(proof, "dom sep: app1")
and H_2(proof, "dom sep: app2)
to get different VRF output.
I believe we knew it's not the most standard-compliant API but it's less error-prone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that the verify
function will take proof, output, ...
and internally check evaluate(proof) =? output
as an extra safety check?
I'm against that. It would mean that a user (potentially) runs the evaluate
sub-routing twice - first to actually get the VRF output and second to verify that a proof is valid.
Unless the only scenario when someone calls verify(proof, output, ...)
is when they are provided both proof
and output
by a prover - then the "safe" API would make sense.
But I can imagine that a more likely scenario is when the prover produces a proof, runs evaluate for themselves, and only posts/shares a proof
without the output
- leaving it for the verifier to run evaluate
for themselves (once!) and then finally verify(proof, ...)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can have 2 verify APIs, the new set of APIs is as follows:
- prove(pp, sec_key, input) -> proof
- proof_to_hash(pp, proof) -> output
- evaluate(pp, sec_key, input) -> output (i.e. proof_to_hash(pp, prove(pp, sec_key, input)))
- verify(pp, pub_key, proof, input) -> (bit, output) (this API also outputs hash as the hash is much cheaper than sig verification and the user doesn't need to call
proof_to_hash
again to obtain the hash) - verify_hash_consistency(pp, proof, output) -> bit (note that this API is also cheap as it doesn't check signature)
The APIs 1..4 are exactly identical to the standard (https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/), the API 5 is used to address the issue mentioned above where the prover and verifier may use an inconsistent hashing scheme so that their outputs are inconsistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually now I prefer to remove API 5 as the work can be done in API 4: so we just modify the API 4 as follows:
verify(pp, pub_key, proof, input, output') -> (bit, output)
Since API 4 anyway computes output
itself, it can add an additional equality check that output' ?= output
, which is almost for free.
cc @alxiong @tessico
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we should return (bit, output)
from the verify()
function. I missed that, sorry.
I would argue, however, that there might be cases where output
is not known beforehand and cannot be used as input to the verify
routine (unless the user explicitly calls proof_to_hash
first). Please correct me if that's never the case.
If it's so, we would have these alternatives:
- We give
output'
anOption<Self::Output>
type, i.e.verify(pp, pub_key, proof, input, option(output')) -> (bit, output)
. If available, we perform the check, and otherwise just return the result. - We delegate correctness checking to the user. This option is fully IRTF draft compatible.
I've got an example of alternative 2 here. The nice thing is that Rust can help with ensuring that output is used, e.g. by the #[must_use]
attribute, as shown in the v2 branch. This is my preferred approach. There are numerous ways that users can screw up the protocol, I'm not sure whether we should try to prevent against such fundamental mistakes as using a wrong hash function.
Also as @alxiong suggested I've just created a new branch instead of a branch in a fork. For the sake of keeping these discussions I guess we can keep this PR as is now? There's a branch with the same name and I intend to keep them in-sync if people wanna check it out directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If we use alternative 2 (over alternative 1), I'm not fully sure how the user would be able to check that it's in sync with the prover.
If the prover shares only proof
, and doesn't provide the output (e.g. only proof is posted on-chain, or some other way), then the verifier has no way to ensure sync anyway.
If the prover shares (proof, output')
, then the verifier can call verify(proof, ...) = (bit, output)
and for themselves check if output' == output
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's still have
evaluate
API but provide a default implementation by composingprove
andproof_to_hash
So currently I just renamed evaluate
-> proof_to_hash
.
You saying that we add an extra evaluate(secret_key, input)
function corresponding to VRF_hash
in the draft as a convenience, right? Would you add that for the Vrf
trait or specifically for the BLS VRF as a struct method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You saying that we add an extra evaluate(secret_key, input) function corresponding to VRF_hash in the draft as a convenience, right? Would you add that for the Vrf trait or specifically for the BLS VRF as a struct method?
Yes, and add it to Vrf trait
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chancharles92 Done: 8513626
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused, is that commit on this PR or on another branch @tessico ?
primitives/src/vrf/blsvrf.rs
Outdated
let message_bad = [1u8; 32]; | ||
sign_and_verify::<BLSVRFScheme, Sha256>(&message); | ||
failed_verification::<BLSVRFScheme, Sha256>(&message, &message_bad); | ||
let message = vec![0u8; 32]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also test the correctness of the hash output somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, will add
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in b9f3c50.
primitives/src/vrf/mod.rs
Outdated
&self, | ||
pp: &Self::PublicParameter, | ||
proof: &Self::Proof, | ||
public_key: &Self::PublicKey, | ||
input: &Self::Input, | ||
) -> Result<bool, PrimitivesError>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can have 2 verify APIs, the new set of APIs is as follows:
- prove(pp, sec_key, input) -> proof
- proof_to_hash(pp, proof) -> output
- evaluate(pp, sec_key, input) -> output (i.e. proof_to_hash(pp, prove(pp, sec_key, input)))
- verify(pp, pub_key, proof, input) -> (bit, output) (this API also outputs hash as the hash is much cheaper than sig verification and the user doesn't need to call
proof_to_hash
again to obtain the hash) - verify_hash_consistency(pp, proof, output) -> bit (note that this API is also cheap as it doesn't check signature)
The APIs 1..4 are exactly identical to the standard (https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/), the API 5 is used to address the issue mentioned above where the prover and verifier may use an inconsistent hashing scheme so that their outputs are inconsistent.
Composing prove and proof_to_hash calls
- test the hash output directly - test evaluation(sk, m) = proof_to_hash(prove(sk, m)) - generate random test vectors
I've updated the branches, and also references in the discussion threads here, such that that all links point to commits in this branch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! (feel free to add trait bounds of associate types on trait Vrf
and trait SignatureScheme
in a separate PR)
Description
closes: #125
Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.
Pending
section inCHANGELOG.md
Files changed
in the GitHub PR explorer